"""
第4章：依赖注入系统 - 实战练习代码
配合 chapter04_dependencies.md 讲义使用

运行方式：
cd /Users/baimu/PycharmProjects/fastApiProject3
uvicorn stage1_basics.chapter04_dependencies_practice:app --reload --port 8005

访问：
- Swagger UI: http://127.0.0.1:8005/docs
- 根路径: http://127.0.0.1:8005/

测试认证：
- 普通用户 token: Bearer token_user_1
- 管理员 token: Bearer token_admin_1
"""

from fastapi import FastAPI, Depends, HTTPException, Header, Query, status
from pydantic import BaseModel
from typing import Optional, Generator, Dict, List
from datetime import datetime
import time

# ========== 应用初始化 ==========

app = FastAPI(
    title="第4章：依赖注入系统练习",
    description="FastAPI 依赖注入的完整示例",
    version="1.0.0",
)

# ========== 模拟数据 ==========

# 模拟用户数据库
USERS_DB = {
    "token_user_1": {"id": 1, "username": "john", "email": "john@example.com", "role": "user"},
    "token_user_2": {"id": 2, "username": "jane", "email": "jane@example.com", "role": "user"},
    "token_admin_1": {"id": 3, "username": "admin", "email": "admin@example.com", "role": "admin"},
}

# 模拟文章数据
POSTS_DB: List[dict] = [
    {"id": 1, "title": "FastAPI 入门", "content": "内容1", "author_id": 1},
    {"id": 2, "title": "Python 异步编程", "content": "内容2", "author_id": 2},
]

# ========== 1. 简单依赖 ==========


def get_query_param(q: Optional[str] = None):
    """简单的查询参数依赖"""
    return q


@app.get("/demo/simple-dependency", tags=["简单依赖"])
async def simple_dependency(query: Optional[str] = Depends(get_query_param)):
    """
    简单依赖示例
    
    演示最基本的依赖注入用法
    """
    return {"query": query, "note": "这是一个简单的依赖"}


# ========== 2. 可复用的依赖 ==========


def common_parameters(
    q: Optional[str] = None,
    skip: int = Query(0, ge=0),
    limit: int = Query(10, ge=1, le=100)
):
    """通用查询参数"""
    return {"q": q, "skip": skip, "limit": limit}


@app.get("/demo/users", tags=["可复用依赖"])
async def list_users(params: dict = Depends(common_parameters)):
    """使用通用查询参数"""
    return {
        "type": "users",
        "params": params,
        "data": ["user1", "user2", "user3"][params["skip"]:params["skip"] + params["limit"]]
    }


@app.get("/demo/posts", tags=["可复用依赖"])
async def list_posts(params: dict = Depends(common_parameters)):
    """同样使用通用查询参数"""
    return {
        "type": "posts",
        "params": params,
        "data": ["post1", "post2", "post3"][params["skip"]:params["skip"] + params["limit"]]
    }


# ========== 3. 类作为依赖 ==========


class PaginationParams:
    """分页参数类"""
    
    def __init__(
        self,
        page: int = Query(1, ge=1, description="页码"),
        page_size: int = Query(10, ge=1, le=100, description="每页数量")
    ):
        self.page = page
        self.page_size = page_size
        self.skip = (page - 1) * page_size
        self.limit = page_size
    
    def paginate(self, items: list) -> dict:
        """对列表进行分页"""
        total = len(items)
        items_page = items[self.skip:self.skip + self.limit]
        
        return {
            "items": items_page,
            "pagination": {
                "page": self.page,
                "page_size": self.page_size,
                "total": total,
                "total_pages": (total + self.page_size - 1) // self.page_size,
                "has_next": self.skip + self.limit < total
            }
        }


@app.get("/demo/paginated-items", tags=["类依赖"])
async def paginated_items(pagination: PaginationParams = Depends()):
    """
    使用类作为依赖
    
    演示如何使用类来组织依赖逻辑
    """
    all_items = [f"item_{i}" for i in range(1, 51)]  # 50个项目
    return pagination.paginate(all_items)


# ========== 4. yield 依赖（资源管理）==========


class DatabaseSession:
    """模拟数据库会话"""
    
    def __init__(self):
        self.session_id = datetime.now().strftime("%Y%m%d%H%M%S%f")
        print(f"📁 [{self.session_id}] 打开数据库连接")
        self.is_active = True
    
    def query(self, sql: str):
        if not self.is_active:
            raise Exception("数据库连接已关闭")
        print(f"🔍 [{self.session_id}] 执行查询: {sql}")
        return f"查询结果: {sql}"
    
    def close(self):
        if self.is_active:
            print(f"🔒 [{self.session_id}] 关闭数据库连接")
            self.is_active = False


def get_db() -> Generator[DatabaseSession, None, None]:
    """数据库依赖（使用 yield）"""
    db = DatabaseSession()
    try:
        yield db
    finally:
        db.close()


@app.get("/demo/database-query", tags=["yield依赖"])
async def database_query(db: DatabaseSession = Depends(get_db)):
    """
    使用 yield 依赖进行资源管理
    
    数据库连接会在请求结束后自动关闭
    """
    result = db.query("SELECT * FROM users")
    return {"result": result, "note": "查看控制台输出，看数据库连接的生命周期"}


# ========== 5. 嵌套依赖 ==========


def get_token(authorization: str = Header(..., description="Bearer token")):
    """第1层：从请求头获取 token"""
    if not authorization.startswith("Bearer "):
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid authorization format. Use: 'Bearer <token>'"
        )
    token = authorization[7:]  # 移除 "Bearer "
    print(f"🔑 获取到 token: {token}")
    return token


def verify_token(token: str = Depends(get_token)):
    """第2层：验证 token 是否有效"""
    if token not in USERS_DB:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid or expired token"
        )
    print(f"✅ Token 验证成功")
    return token


def get_current_user(token: str = Depends(verify_token)):
    """第3层：获取当前登录用户"""
    user = USERS_DB[token]
    print(f"👤 当前用户: {user['username']} ({user['role']})")
    return user


@app.get("/demo/me", tags=["嵌套依赖"])
async def read_current_user(user: dict = Depends(get_current_user)):
    """
    嵌套依赖示例
    
    依赖链：get_token → verify_token → get_current_user
    
    测试方法：
    在 Swagger UI 中点击 🔓 按钮，输入：
    - token_user_1  （普通用户）
    - token_admin_1 （管理员）
    """
    return {
        "user": user,
        "note": "查看控制台输出，观察依赖的执行顺序"
    }


# ========== 6. 权限检查依赖 ==========


def require_admin(user: dict = Depends(get_current_user)):
    """检查是否为管理员"""
    if user["role"] != "admin":
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail=f"需要管理员权限。当前角色: {user['role']}"
        )
    print(f"🛡️ 管理员权限验证通过")
    return user


@app.get("/demo/admin/dashboard", tags=["权限检查"])
async def admin_dashboard(admin: dict = Depends(require_admin)):
    """
    管理员专属路由
    
    只有管理员可以访问
    
    测试方法：
    - 使用 token_admin_1 可以访问
    - 使用 token_user_1 会被拒绝（403错误）
    """
    return {
        "message": f"欢迎，{admin['username']} 管理员！",
        "admin": admin,
        "dashboard_data": {
            "total_users": len(USERS_DB),
            "total_posts": len(POSTS_DB)
        }
    }


# ========== 7. 请求日志记录器 ==========


class RequestLogger:
    """请求日志记录器"""
    
    def __init__(self):
        self.start_time = time.time()
        self.request_id = datetime.now().strftime("%Y%m%d%H%M%S%f")
        self.logs = []
        print(f"\n{'='*60}")
        print(f"📝 [{self.request_id}] 🚀 请求开始")
    
    def log(self, message: str):
        elapsed = time.time() - self.start_time
        log_entry = f"[{elapsed:.3f}s] {message}"
        self.logs.append(log_entry)
        print(f"📝 [{self.request_id}] {log_entry}")
    
    def finish(self):
        elapsed = time.time() - self.start_time
        print(f"📝 [{self.request_id}] ✅ 请求完成 (总耗时: {elapsed:.3f}s)")
        print(f"{'='*60}\n")


def get_logger() -> Generator[RequestLogger, None, None]:
    """日志记录器依赖"""
    logger = RequestLogger()
    try:
        yield logger
    finally:
        logger.finish()


@app.get("/demo/logged-operation", tags=["日志记录"])
async def logged_operation(logger: RequestLogger = Depends(get_logger)):
    """
    带日志记录的操作
    
    查看控制台输出，观察完整的请求生命周期
    """
    import asyncio
    
    logger.log("开始操作")
    
    logger.log("步骤1: 验证参数")
    await asyncio.sleep(0.2)
    
    logger.log("步骤2: 查询数据库")
    await asyncio.sleep(0.3)
    
    logger.log("步骤3: 处理数据")
    await asyncio.sleep(0.1)
    
    logger.log("步骤4: 返回结果")
    
    return {
        "message": "操作完成",
        "logs": logger.logs,
        "note": "查看控制台的详细日志"
    }


# ========== 8. 组合多个依赖 ==========


@app.get("/api/posts", tags=["实战示例"])
async def list_posts(
    # 认证
    user: dict = Depends(get_current_user),
    # 分页
    pagination: PaginationParams = Depends(),
    # 数据库
    db: DatabaseSession = Depends(get_db),
    # 日志
    logger: RequestLogger = Depends(get_logger)
):
    """
    组合多个依赖的完整示例
    
    这个路由同时使用了：
    - 用户认证
    - 分页
    - 数据库连接
    - 日志记录
    
    展示了依赖注入的强大之处！
    """
    logger.log(f"用户 {user['username']} 请求文章列表")
    
    # 模拟数据库查询
    logger.log("从数据库查询文章")
    db.query("SELECT * FROM posts")
    
    # 分页
    logger.log(f"应用分页: page={pagination.page}, size={pagination.page_size}")
    result = pagination.paginate(POSTS_DB)
    
    logger.log("返回结果")
    
    return {
        "user": user['username'],
        **result
    }


# ========== 9. 实战：完整的用户管理 API ==========


class User(BaseModel):
    """用户模型"""
    username: str
    email: str


class UserCreate(User):
    """创建用户"""
    password: str


class UserResponse(User):
    """用户响应"""
    id: int
    role: str


# 模拟用户数据库
users_storage: Dict[int, dict] = {
    1: {"id": 1, "username": "john", "email": "john@example.com", "password": "hash1", "role": "user"},
    2: {"id": 2, "username": "jane", "email": "jane@example.com", "password": "hash2", "role": "user"},
    3: {"id": 3, "username": "admin", "email": "admin@example.com", "password": "hash3", "role": "admin"},
}
next_user_id = 4


@app.get(
    "/api/users",
    response_model=List[UserResponse],
    tags=["用户管理API"]
)
async def get_users(
    pagination: PaginationParams = Depends(),
    current_user: dict = Depends(get_current_user)
):
    """
    获取用户列表
    
    需要登录
    """
    users = list(users_storage.values())
    return pagination.paginate(users)


@app.get(
    "/api/users/{user_id}",
    response_model=UserResponse,
    tags=["用户管理API"]
)
async def get_user(
    user_id: int,
    current_user: dict = Depends(get_current_user)
):
    """
    获取单个用户
    
    需要登录
    """
    if user_id not in users_storage:
        raise HTTPException(status_code=404, detail="用户不存在")
    return users_storage[user_id]


@app.post(
    "/api/users",
    response_model=UserResponse,
    status_code=status.HTTP_201_CREATED,
    tags=["用户管理API"]
)
async def create_user(
    user: UserCreate,
    admin: dict = Depends(require_admin)
):
    """
    创建新用户
    
    只有管理员可以创建用户
    """
    global next_user_id
    
    # 检查用户名是否存在
    for u in users_storage.values():
        if u["username"] == user.username:
            raise HTTPException(
                status_code=409,
                detail=f"用户名 '{user.username}' 已存在"
            )
    
    # 创建用户
    new_user = {
        "id": next_user_id,
        "username": user.username,
        "email": user.email,
        "password": f"hashed_{user.password}",
        "role": "user"
    }
    
    users_storage[next_user_id] = new_user
    next_user_id += 1
    
    return new_user


@app.delete(
    "/api/users/{user_id}",
    status_code=status.HTTP_204_NO_CONTENT,
    tags=["用户管理API"]
)
async def delete_user(
    user_id: int,
    admin: dict = Depends(require_admin)
):
    """
    删除用户
    
    只有管理员可以删除用户
    """
    if user_id not in users_storage:
        raise HTTPException(status_code=404, detail="用户不存在")
    
    # 不能删除管理员自己
    if user_id == admin["id"]:
        raise HTTPException(status_code=400, detail="不能删除自己")
    
    del users_storage[user_id]
    return None


# ========== 10. 依赖缓存演示 ==========


call_count = 0


def expensive_operation():
    """耗时操作"""
    global call_count
    call_count += 1
    print(f"💰 执行耗时操作（第 {call_count} 次调用）")
    return f"result_{call_count}"


@app.get("/demo/dependency-cache", tags=["依赖缓存"])
async def dependency_cache(
    dep1: str = Depends(expensive_operation),
    dep2: str = Depends(expensive_operation),  # ← 默认会使用缓存
    dep3: str = Depends(expensive_operation)
):
    """
    依赖缓存示例
    
    FastAPI 会缓存同一个请求中的依赖结果
    
    查看控制台输出：
    - expensive_operation 只会被调用一次
    - 三个参数得到相同的结果
    """
    return {
        "dep1": dep1,
        "dep2": dep2,
        "dep3": dep3,
        "note": "查看控制台，观察函数只被调用一次",
        "all_same": dep1 == dep2 == dep3
    }


# ========== 根路径 ==========


@app.get("/", tags=["根路径"])
async def root():
    """
    根路径
    
    返回欢迎信息和使用说明
    """
    return {
        "message": "欢迎来到第4章：依赖注入系统练习！",
        "features": [
            "简单依赖",
            "可复用依赖",
            "类依赖",
            "yield 依赖（资源管理）",
            "嵌套依赖",
            "权限检查",
            "日志记录",
            "组合多个依赖"
        ],
        "authentication": {
            "note": "很多端点需要认证，点击右上角🔓按钮输入token",
            "tokens": {
                "普通用户": "token_user_1",
                "管理员": "token_admin_1"
            }
        },
        "try_these": {
            "简单示例": "/demo/me",
            "管理员面板": "/demo/admin/dashboard",
            "完整示例": "/api/posts",
            "用户管理": "/api/users"
        },
        "docs": "/docs"
    }


# ========== 启动信息 ==========

if __name__ == "__main__":
    print("=" * 70)
    print("📚 第4章：依赖注入系统 - 实战练习")
    print("=" * 70)
    print("\n启动命令:")
    print("uvicorn stage1_basics.chapter04_dependencies_practice:app --reload --port 8005\n")
    print("访问地址:")
    print("- Swagger UI: http://127.0.0.1:8005/docs")
    print("- ReDoc: http://127.0.0.1:8005/redoc")
    print("- 根路径: http://127.0.0.1:8005/")
    print("\n🔐 认证信息:")
    print("在 Swagger UI 中点击右上角 🔓 Authorize 按钮，输入以下token：")
    print("- 普通用户: token_user_1")
    print("- 管理员:   token_admin_1")
    print("\n学习重点:")
    print("1. 理解依赖注入如何避免代码重复")
    print("2. 掌握 yield 依赖进行资源管理")
    print("3. 学会构建依赖链（嵌套依赖）")
    print("4. 使用依赖进行权限控制")
    print("5. 组合多个依赖构建复杂功能")
    print("\n💡 提示:")
    print("观察控制台输出，查看依赖的执行过程和资源管理！")
    print("\n" + "=" * 70)

